﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Globalization;
using System.IO;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
using System.Web.Compilation;
using Vit.Web.Models;
using Vit.Web.Services;

namespace Vit.Web.Mvc
{
    public class VitViewEngine : WebFormViewEngine
    {
        private static readonly string[] _emptyLocations;

        static VitViewEngine()
        {
            _emptyLocations = new string[0];
        }

        public VitViewEngine()
        {
            base.MasterLocationFormats = new string[] { 
                "~/Sites/{3}/Views/{1}/{0}.master", 
                "~/Sites/{3}/Views/Shared/{0}.master", 
                "~/Views/{1}/{0}.master", 
                "~/Views/Shared/{0}.master" 
            };
            base.AreaMasterLocationFormats = new string[] { 
                "~/Sites/{3}/Areas/{2}/Views/{1}/{0}.master", 
                "~/Sites/{3}/Areas/{2}/Views/Shared/{0}.master",
                "~/Views/Areas/Vit.{2}/{1}/{0}.master", 
                "~/Views/Areas/Vit.{2}/Shared/{0}.master" 
            };
            base.ViewLocationFormats = new string[] { 
                "~/Sites/{3}/Views/{1}/{0}.aspx", 
                "~/Sites/{3}/Views/Shared/{0}.aspx",
                "~/Views/{1}/{0}.aspx", 
                "~/Views/Shared/{0}.aspx"
            };
            base.AreaViewLocationFormats = new string[] { 
                "~/Sites/{3}/Areas/{2}/Views/{1}/{0}.aspx", 
                "~/Sites/{3}/Areas/{2}/Views/Shared/{0}.aspx", 
                "~/Views/Areas/Vit.{2}/{1}/{0}.aspx", 
                "~/Views/Areas/Vit.{2}/Shared/{0}.aspx" 
            };
            base.PartialViewLocationFormats = new string[] { 
                "~/Sites/{3}/Views/{1}/{0}.ascx", 
                "~/Sites/{3}/Views/Shared/{0}.ascx", 
                "~/Views/{1}/{0}.ascx", 
                "~/Views/Shared/{0}.ascx" 
            };
            base.AreaPartialViewLocationFormats = new string[] { 
                "~/Sites/{3}/Areas/{2}/Views/{1}/{0}.ascx", 
                "~/Sites/{3}/Areas/{2}/Views/Shared/{0}.ascx", 
                "~/Views/Areas/Vit.{2}/{1}/{0}.ascx", 
                "~/Views/Areas/Vit.{2}/Shared/{0}.ascx" 
            };
        }

        public override ViewEngineResult FindPartialView(ControllerContext controllerContext, string partialViewName, bool useCache)
        {
            string[] searchedLocations;
            if (controllerContext == null)
            {
                throw new ArgumentNullException("controllerContext");
            }
            if (string.IsNullOrEmpty(partialViewName))
            {
                throw new ArgumentNullException("partialViewName");
            }
            string controllerName = controllerContext.RouteData.GetRequiredString("controller");
            string viewPath = this.GetPath(controllerContext, this.PartialViewLocationFormats, this.AreaPartialViewLocationFormats, "PartialViewLocationFormats", partialViewName, controllerName, "Partial", useCache, out searchedLocations);
            if (string.IsNullOrEmpty(viewPath))
            {
                return new ViewEngineResult(searchedLocations);
            }
            return new ViewEngineResult(this.CreatePartialView(controllerContext, viewPath), this);
        }

        public override ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache)
        {
            string[] searchedViewLocations;
            string[] searchedMasterLocations;
            if (controllerContext == null)
            {
                throw new ArgumentNullException("controllerContext");
            }
            if (string.IsNullOrEmpty(viewName))
            {
                throw new ArgumentNullException("viewName");
            }
            string controllerName = controllerContext.RouteData.GetRequiredString("controller");
            string viewPath = this.GetPath(controllerContext, this.ViewLocationFormats, this.AreaViewLocationFormats, "ViewLocationFormats", viewName, controllerName, "View", useCache, out searchedViewLocations);
            string masterPath = this.GetPath(controllerContext, this.MasterLocationFormats, this.AreaMasterLocationFormats, "MasterLocationFormats", masterName, controllerName, "Master", useCache, out searchedMasterLocations);
            if (!string.IsNullOrEmpty(viewPath) && (!string.IsNullOrEmpty(masterPath) || string.IsNullOrEmpty(masterName)))
            {
                return new ViewEngineResult(this.CreateView(controllerContext, viewPath, masterPath), this);
            }
            return new ViewEngineResult(searchedViewLocations.Union<string>(searchedMasterLocations));
        }

        private string CreateCacheKey(string prefix, string name, string controllerName, string areaName)
        {
            return string.Format(CultureInfo.InvariantCulture, ":ViewCacheEntry:{0}:{1}:{2}:{3}:{4}:", new object[] { base.GetType().AssemblyQualifiedName, prefix, name, controllerName, areaName });
        }

        private string GetPath(ControllerContext controllerContext, string[] locations, string[] areaLocations, string locationsPropertyName, string name, string controllerName, string cacheKeyPrefix, bool useCache, out string[] searchedLocations)
        {
            searchedLocations = _emptyLocations;
            if (string.IsNullOrEmpty(name))
            {
                return string.Empty;
            }
            string areaName = controllerContext.RouteData.DataTokens["area"] as string ?? "";
            bool isSpecificPath = IsSpecificPath(name);
            string[] viewLocations = locations.Union(areaLocations).ToArray();
            string key = this.CreateCacheKey(cacheKeyPrefix, name, isSpecificPath ? string.Empty : controllerName, areaName);
            if (useCache)
            {
                return this.ViewLocationCache.GetViewLocation(controllerContext.HttpContext, key);
            }
            if (!isSpecificPath)
            {
                return this.GetPathFromGeneralName(controllerContext, viewLocations, name, controllerName, areaName, key, ref searchedLocations);
            }
            return this.GetPathFromSpecificName(controllerContext, name, key, ref searchedLocations);
        }

        private string GetPathFromGeneralName(ControllerContext controllerContext, string[] locations, string name, string controllerName, string areaName, string cacheKey, ref string[] searchedLocations)
        {
            string virtualPath = string.Empty;
            WebSite site = ((Vit.Web.VitApplication)HttpContext.Current.ApplicationInstance).CurrentSite;
            searchedLocations = new string[locations.Length];
            for (int i = 0; i < locations.Length; i++)
            {
                string path = string.Format(locations[i], name, controllerName, areaName, site.Name);
                try
                {
                    if (this.FileExists(controllerContext, path))
                    {
                        searchedLocations = _emptyLocations;
                        virtualPath = path;
                        this.ViewLocationCache.InsertViewLocation(controllerContext.HttpContext, cacheKey, virtualPath);
                        return virtualPath;
                    }
                    searchedLocations[i] = path;
                }
                catch
                {
                    continue;
                }
            }
            return virtualPath;
        }

        private string GetPathFromSpecificName(ControllerContext controllerContext, string name, string cacheKey, ref string[] searchedLocations)
        {
            string virtualPath = name;
            if (!this.FileExists(controllerContext, name))
            {
                virtualPath = string.Empty;
                searchedLocations = new string[] { name };
            }
            this.ViewLocationCache.InsertViewLocation(controllerContext.HttpContext, cacheKey, virtualPath);
            return virtualPath;
        }

        private static bool IsSpecificPath(string name)
        {
            char ch = name[0];
            if (ch != '~')
            {
                return (ch == '/');
            }
            return true;
        }
    }
}
